package com.tocici.recommender;

import com.tocici.domain.bo.UserBehaviorBo;
import com.tocici.domain.bo.UserBo;
import com.tocici.domain.vo.MusicVo;
import com.tocici.domain.vo.UserBehaviorVo;
import com.tocici.domain.vo.UserVo;
import com.tocici.mapper.MusicMapper;
import com.tocici.service.IMusicService;
import com.tocici.service.IUserBehaviorService;
import com.tocici.service.IUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

import static com.tocici.recommender.Apriori.apriori;
import static com.tocici.recommender.Apriori.getSubsets;


@Component
@RequiredArgsConstructor
public class AprioriRecommender {

    private final IUserBehaviorService userBehaviorService;

    private final IUserService userService;

    private final MusicMapper mapper;

    public List<MusicVo> recommender(Long userId){
        List<UserVo> userVos = userService.queryList(new UserBo());
        List<Set<String>> transational = new ArrayList<>();
        userVos.forEach(vo -> {
            UserBehaviorBo bo = new UserBehaviorBo();
            bo.setUserId(vo.getUserId());
            List<String> userBehaviors = userBehaviorService.queryList(bo).stream().map(behavior -> behavior.getMusicId().toString()).toList();
            Set<String> behaviorSet = new HashSet<>(userBehaviors);
            transational.add(behaviorSet);

        });
        List<Map<Set<String>, Integer>> apriori = apriori(transational, 0.4);

        List<AssociationRule> associationRules = generateRules(apriori, 0.6);
        UserBehaviorBo bo = new UserBehaviorBo();
        bo.setUserId(userId);
        UserBehaviorVo userBehaviorVo = userBehaviorService.queryList(bo).stream().findFirst().get();
        HashSet<String> current = new HashSet<>(Arrays.asList(userBehaviorVo.getMusicId().toString()));
        List<String> recommendations = generateRecommendations(current, associationRules, 3);
        System.out.println("recommendations = " + recommendations);
        List<Long> list = recommendations.stream().map(re -> Long.valueOf(re)).toList();
        List<MusicVo> musicVos = mapper.selectVoByIds(list);
        System.out.println("musicVos = " + musicVos);

        return musicVos;

    }

    public static List<AssociationRule> generateRules(List<Map<Set<String>, Integer>> frequentItemsets,
                                                      double minConfidence) {
        List<AssociationRule> rules = new ArrayList<>();

        // 从k>=2的项集开始生成规则
        for (int k = 1; k < frequentItemsets.size(); k++) {
            Map<Set<String>, Integer> itemsetMap = frequentItemsets.get(k);

            for (Map.Entry<Set<String>, Integer> entry : itemsetMap.entrySet()) {
                Set<String> itemset = entry.getKey();
                int totalCount = entry.getValue();

                // 生成所有可能的规则组合
                List<Set<String>> subsets = getSubsets(itemset, itemset.size() - 1);

                for (Set<String> antecedent : subsets) {
                    Set<String> consequent = new HashSet<>(itemset);
                    consequent.removeAll(antecedent);

                    // 查找前件的支持度
                    int antecedentCount = findSupportCount(antecedent, frequentItemsets);

                    double confidence = (double) totalCount / antecedentCount;
                    if (confidence >= minConfidence) {
                        rules.add(new AssociationRule(antecedent, consequent, confidence));
                    }
                }
            }
        }

        return rules;
    }

    // 辅助方法：在频繁项集中查找支持度
    private static int findSupportCount(Set<String> itemset, List<Map<Set<String>, Integer>> frequentItemsets) {
        int k = itemset.size() - 1;
        if (k < 0 || k >= frequentItemsets.size()) return 0;

        return frequentItemsets.get(k).getOrDefault(itemset, 0);
    }

    public static List<String> generateRecommendations(Set<String> currentItems,
                                                       List<AssociationRule> rules,
                                                       int topN) {
        Map<String, Double> recommendationScores = new HashMap<>();

        for (AssociationRule rule : rules) {
            if (rule.getAntecedent().containsAll(currentItems)) {
                for (String item : rule.getConsequent()) {
                    if (!currentItems.contains(item)) {
                        recommendationScores.merge(item, rule.getConfidence(), Double::sum);
                    }
                }
            }
        }

        // 按置信度排序取TopN
        return recommendationScores.entrySet().stream()
            .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
            .limit(topN)
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());
    }

}
